Skip to content

fix: return generic error for workspace resolution failures (#17)#19

Open
mgoldsborough wants to merge 3 commits into
mainfrom
fix/issue-17-generic-workspace-errors
Open

fix: return generic error for workspace resolution failures (#17)#19
mgoldsborough wants to merge 3 commits into
mainfrom
fix/issue-17-generic-workspace-errors

Conversation

@mgoldsborough
Copy link
Copy Markdown
Contributor

Summary

  • Workspace resolution returned distinct responses for non-existent workspaces (400, "Workspace "ws_x" not found") vs unauthorized access (403, "Access denied: not a member of ws_x"). The combination of status codes and ID-bearing messages let any authenticated caller probe for workspace existence and membership.
  • Collapsed both cases to a single 403 with the generic message "Access denied to workspace." and no workspace ID echo. Input validation paths (invalid format, multiple-workspace ambiguity) are left alone — they do not signal existence of other tenants' workspaces.

Test plan

  • `bun test test/unit/api/ test/integration/workspaces-identity.test.ts` — 104 pass
  • `bun run lint` — no new warnings
  • `bun run check` — clean typecheck
  • Updated unit tests assert generic message and 403 status for both paths, and explicitly assert the workspace ID is not echoed

Closes #17

Workspace resolution previously returned distinct responses for
non-existent workspaces (400, "Workspace \"ws_x\" not found") and
unauthorized access (403, "Access denied: not a member of ws_x").
The combination of distinct status codes and ID-bearing messages
let any authenticated caller probe for workspace existence and
membership.

Collapse both to a single generic 403 "Access denied to workspace."
with no workspace ID echo. Input validation paths (invalid format,
multiple-workspace ambiguity) are untouched — they do not signal
existence of other tenants' workspaces.
@mgoldsborough mgoldsborough force-pushed the fix/issue-17-generic-workspace-errors branch from 724f7df to 2c91607 Compare May 30, 2026 17:51
…sage

The Stage 1 cross-workspace conversation test asserted the old
descriptive 'not a member' message on the non-member 403. The generic
workspace-resolution error (issue #17) replaces it with a fixed
'Access denied to workspace.' that does not echo the workspace id.
Update the assertion to match and add a no-ID-echo check.
@mgoldsborough mgoldsborough added security Security hardening and defense-in-depth area/api Code area: api labels May 30, 2026
The original fix collapsed not-found/forbidden into a generic 403 in
resolveWorkspace, but the http-proxy route
(/v1/ws/:wsId/apps/:bundle/:mount/*) reimplements workspace resolution
inline — it reads wsId from the path param, not the X-Workspace-Id
header — and still returned a distinguishable 400 "Workspace <id> not
found" vs 403 "not a member of <id>" with the id echoed. Behind
requireAuth with an attacker-suppliable path param, that's the same
existence/membership oracle issue #17 describes, on a second surface.

- Extract isWorkspaceMember(ws, userId) as the single membership
  predicate (accepts nullish ws so callers fold not-found into
  not-member). Route resolveWorkspace, the proxy route, and the
  bootstrap filter through it so the two states can't drift apart on
  one surface and stay folded on another.
- Proxy route now returns one generic 403 "Access denied to workspace."
  (no id echo) for both unknown and non-member workspaces for
  authenticated callers; server-side logs keep the wsId. The dev-mode
  (no identity) not-found path stays a plain non-leaking response.
- Update proxy-route integration tests: non-existent now returns the
  same generic 403 as non-member, with no id echo.

The MCP /mcp surface (UnknownWorkspace vs WorkspaceAccessDenied) keeps
its distinct structured data.reason discriminators — that split is a
deliberate, documented orchestrator contract; revisiting it is tracked
separately.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/api Code area: api security Security hardening and defense-in-depth

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use generic error messages for workspace resolution failures

1 participant